class Map
  BACK_DIRS = ['n s','e w','ne sw','nw se','u d'].inject({}) {|s,v| d=v.split;s[d[0]]=d[1];s[d[1]]=d[0];s}
  DIR_LONG = {'n' => 'north', 's' => 'south', 'e' => 'east', 'w' => 'west', 'u' => 'up', 'd' => 'down'}
  
  @@id_mapping ||= Hash.new
  @@id_mapping_rev ||= Hash.new
  @@portals ||= Hash.new
  
  def self.init
    $db.execute("SELECT id FROM Area") {|a| Area.init(a)}
    $db.execute("SELECT id FROM Room") {|r| Room.init(r)}
  end
  
  def self.set_area(num, texture, x, y, z, flags, name, col)
    if Area.exist?(num)
      Area.set_current(num)
    else
      echo "-----\nNEW AREA WAT DO???\n-----"
    end
  end
  def self.set_room(num, name, zone, terrain, details, exits, coord)
    if Area.current.nil? || Area.current.num != zone
      $app.connection.send_gmcp_packet('request area')
    end
    unless Room.exist?(num)
      Room.create(num, name, zone, terrain, details, exits, coord)
    end
    Room.set_current(num, exits)
  end
  
  ####### CHECK FUNCTIONS #######
  def self.same_area(r1, r2); r1.area_id == r2.area_id ||
                              (r1.coord_x == r2.coord_x && r1.coord_y == r2.coord_y && r1.coord_id == r2.coord_id) end
  
  ####### ID MAPPING STUFF #######
  def self.set_portals(dict); @@portals = dict end
  
  def self.add_id_mapping(dict); dict.each {|k,v| @@id_mapping[k] = v;@@id_mapping_rev[v] = k} end
  def self.rev_mapping(num); @@id_mapping_rev.key?(num) ? @@id_mapping_rev[num] : num end
  def self.has_mapping?(name); @@id_mapping.key?(name) end
  def self.get_mapping(name)
    return name.to_i if name == '0' || name.to_i != 0
    return @@id_mapping[name] if @@id_mapping.key?(name)
    return $db.get_first_value("SELECT start FROM Area WHERE num=?", name)
  end
  
  ####### PATH FINDING #######
  def self.findpath(source, destination, add_exits = {})
    return [] if source == destination
    previous = Map.dijkstra(source, destination, add_exits)[0]
    return nil unless previous[destination]
    path = [destination]
    path.push(previous[path[-1]]) until previous[path[-1]] == source
    path.reverse!
    return path
  end
  
  def self.use_portal(source, destination = nil)
    add_exits = @@portals.inject({}) {|s,(r, p)|
      p_str, lev = p.split('|')
      s[r] = {'cmd'=>p_str, 'cost'=>4} unless Char.level < lev.to_i
      s
    }
    path = self.findpath(source, destination, add_exits)
    portal = add_exits.find {|k, v| path[0] == k}[1]['cmd']
    [portal, path]
  end
    
  def self.dist_guess(source, destination)
    return 0 if Map.same_area(source, destination)
    return 10 if source.db_id == 26151 || (source.coord_x == 20 && source.coord_y == 30 && source.coord_id == 0)
    if source.coord_id == destination.coord_id
      return source.coord_cont == 1 ? 10 : 30
    else
      return source.coord_cont == 1 ? 20 : 40
    end
  end
  
  def self.dijkstra(source, destination, add_exits = {})
    sr, dr = Room[source], Room[destination]
    visited, shortest, guess, previous, pq = Hash.new, Hash.new, Hash.new, Array.new, [sr]
    
    shortest[sr.db_id] = 0
    guess[sr.db_id] = self.dist_guess(sr, dr)
    while (cr = pq.shift)
      visited[cr.db_id] = true
      
      exits_hash = cr.wayto
      exits_hash = exits_hash.merge(add_exits) if cr.db_id == source
      
      exits_hash.each {|adj_id, info|
        next unless Room.exist?(adj_id)
        nd = shortest[cr.db_id] + (info['cost'] || 8)
        #nd += 0.6 if @@list[v].keys.key?(dir)
        #nd += 0.8 if add_exits.key?(dir)
        if (!shortest.key?(adj_id) || shortest[adj_id] > nd) && (!visited[dr.db_id] || nd < shortest[dr.db_id])
          previous[adj_id] = cr.db_id
          shortest[adj_id] = nd
          guess[adj_id] = nd + self.dist_guess(Room[adj_id], dr) - (Room[previous[cr.db_id]].wayto[cr.db_id] == info['cmd'] ? 0.01 : 0)
          #guess[adj_id] += 1 if Map[source].zone == Map[destination].zone && Map[source].zone != Map[adj_room].zone
          pq.insert(pq.index {|i| guess[adj_id] <= guess[i.db_id]} || pq.size, Room[adj_id])
        end
      }
    end
    return [previous]
  end
end